﻿using System;
using System.Linq;
using System.Collections.Generic;
using LingDong.IndexEntities.Dictionary;
using LingDong.IndexEntities.Document;
using NLog;
using LingDong.IndexEntities.Helper;

namespace LingDong.IndexReader
{
    public class Index : IDisposable
    {
        #region Constructor

        public Index()
        {
            logger = LogManager.GetCurrentClassLogger();
            logger.Debug("Index Consturct.");

            if (!IndexNameHelper.AllIndexFileExists())
            {
                logger.Error("Index doesn't exit");
                throw new ArgumentException("Index doesn't exits: " + IndexNameHelper.GetDictionaryFileName());
            }

            try
            {
                logger.Debug("DictionaryIndex Consturct.");
                dictionaryIndex = new DictionaryIndex();

                logger.Debug("DocumentIndex Consturct.");
                documentIndex = new DocumentIndex();

                totalDocumentCount = documentIndex.GetMaxDocumentID();
                totalTermCount = dictionaryIndex.GetMaxTermID();

                logger.Debug("Index summery: DocNum: {0}, TermNum: {1}", 
                    GetTotalDocumentCount(), GetTotalTermCount());
            }
            catch (Exception ex)
            {
                logger.Error("Error in Index Consturct: {0}", ex.Message);
                throw;
            }
        }

        #endregion


        #region Dictionary
        
        public int GetTotalTermCount()
        {
            return totalTermCount;
        }

        public int GetTermID(string term)
        {
            return dictionaryIndex.GetTermID(term);
        }

        public string GetTermByID(int id)
        {
            return dictionaryIndex.GetTermByID(id);
        }

        /// <summary>
        /// DF
        /// </summary>
        /// <param name="term"></param>
        /// <returns></returns>
        public int GetTermDocumentFrequency(string term)
        {
            return dictionaryIndex.GetTermDocumentFrequency(term);
        }

        public int GetTermDocumentFrequency(int id)
        {
            if (id < 0 || id >= totalDocumentCount)
                return 0;

            return GetTermDocumentFrequency(GetTermByID(id));
        }

        /// <summary>
        /// TF
        /// </summary>
        /// <param name="term"></param>
        /// <returns></returns>
        public int GetTermFrequence(string term)
        {
            return dictionaryIndex.GetTermFrequence(term);
        }

        public int GetTermFrequence(int id)
        {
            if (id < 0 || id >= totalDocumentCount)
                return 0;

            return dictionaryIndex.GetTermFrequence(GetTermByID(id));
        }

        /// <summary>
        /// TF in title
        /// </summary>
        /// <param name="term"></param>
        /// <returns></returns>
        public int GetTermFrequenceInTitle(string term)
        {
            return dictionaryIndex.GetTermFrequenceInTitle(term);
        }

        public int GetTermFrequenceInTitle(int id)
        {
            if (id < 0 || id >= totalDocumentCount)
                return 0;

            return dictionaryIndex.GetTermFrequenceInTitle(GetTermByID(id));
        }

        /// <summary>
        /// POS
        /// </summary>
        /// <param name="term"></param>
        /// <returns></returns>
        public PanGu.POS GetTermPOS(string term)
        {
            return dictionaryIndex.GetTermPOS(term);
        }

        public PanGu.POS GetTermPOS(int id)
        {
            if (id < 0 || id >= totalDocumentCount)
                return 0;

            return dictionaryIndex.GetTermPOS(GetTermByID(id));
        }

        /// <summary>
        /// Type
        /// </summary>
        /// <param name="term"></param>
        /// <returns></returns>
        public PanGu.WordType GetTermType(string term)
        {
            return dictionaryIndex.GetTermType(term);
        }

        public PanGu.WordType GetTermType(int id)
        {
            if (id < 0 || id >= totalDocumentCount)
                return 0;

            return dictionaryIndex.GetTermType(GetTermByID(id));
        }

        /// <summary>
        /// Get inverted list by term
        /// Each Dictionary Pair is (docID, InvertedListEntity)
        /// </summary>
        /// <param name="term"></param>
        /// <returns></returns>
        public Dictionary<int, InvertedListEntity> GetInvertedList(string term)
        {
            return dictionaryIndex.GetInvertedListByTerm(term);
        }

        public Dictionary<int, InvertedListEntity> GetInvertedList(int id)
        {
            if (id < 0 || id >= totalDocumentCount)
                return new Dictionary<int, InvertedListEntity>();

            return dictionaryIndex.GetInvertedListByTerm(GetTermByID(id));
        }

        #endregion
        

        #region Document
        
        public int GetTotalDocumentCount()
        {
            return totalDocumentCount;
        }

        public ParsedDocumentEntity GetParsedDocument(int docID)
        {
            return documentIndex.GetParsedDocumentByID(docID);
        }

        public OriginalDocumentEnity GetOriginalDocument(int docID)
        {
            return documentIndex.GetOriginalDocumentByID(docID);
        }

        public double GetDocumentRank(int docID)
        {
            return documentIndex.GetDocumentRank(docID);
        }

        public List<int> GetTermAppearDocList(string term)
        {
            Dictionary<int, InvertedListEntity> dic = GetInvertedList(term);
            return dic.Select(pair => pair.Key).ToList();
        }

        #endregion

        
        #region Private Members

        private static DictionaryIndex dictionaryIndex;
        private static DocumentIndex documentIndex;
        private static int totalDocumentCount;
        private static int totalTermCount;
        private Logger logger;

        #endregion
        

        #region Disposable Related

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            // protect from being called multiple times.
            if (disposed)
            {
                return;
            }

            if (disposing)
            {
                // clean up all managed resources.
                if (dictionaryIndex != null)
                {
                    dictionaryIndex.Dispose();
                }
                if (documentIndex != null)
                {
                    documentIndex.Dispose();
                }
            }

            disposed = true;
        }

        private bool disposed = false;

        #endregion
    }
}
